﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace Snowdreamist.Windows.Document.Adapter
{
    public abstract class DocumentAdaperBase : IDocumentAdapter
    {
        public abstract Type TargetType { get; }

        public abstract RichTextDocument Convert(object source);

        public abstract RichTextDocument Convert(object source, IDictionary<string, IElementAdapter> elementAdapters);

        protected virtual UIElement GenerateErrorObject(Exception error, Type relatedSourceDocumentType)
        {
            return new ErrorDocumentObject()
            {
                Error = error,
                RelatedSourceElementType = relatedSourceDocumentType
            };
        }

        protected virtual IEnumerable<object> WrapElements(IEnumerable<object> elements, AcceptType acceptType)
        {
            if (acceptType == AcceptType.None)
                throw new InvalidOperationException("Cannot wrap elements");

            if ((acceptType & AcceptType.String) == 0 && elements.OfType<string>().Count() != 0)
            {
                // convert string to Run
                List<object> lst = new List<object>();
                foreach(var e in elements)
                    if (e is string)
                    {
                        lst.Add(new Run((string)e));
                    }
                    else
                    {
                        lst.Add(e);
                    }
                elements = lst;
            }

            if ((acceptType & AcceptType.Inline) == 0 && elements.OfType<Inline>().Count() != 0)
            {
                // combine inline elements to block
                List<object> lst = new List<object>();
                List<Inline> inlines = new List<Inline>();

                foreach (var e in elements)
                {
                    if (e is Inline)
                    {
                        inlines.Add((Inline)e);
                    }
                    else
                    {
                        if (inlines.Count != 0)
                        {
                            // combine
                            Paragraph pgh = new Paragraph();
                            pgh.Inlines.AddRange(inlines);
                            lst.Add(pgh);
                            // clear
                            inlines.Clear();
                        }
                        // add current element
                        lst.Add(e);
                    }
                }

                elements = lst;
            }

            if ((acceptType & AcceptType.Block) == 0 && elements.OfType<Block>().Count() != 0)
            {
                List<object> lst = new List<object>();
                List<Block> blocks = new List<Block>();

                foreach (var e in elements)
                {
                    if (e is Block)
                    {
                        blocks.Add((Block)e);
                    }
                    else
                    {
                        if (blocks.Count != 0)
                        {
                            // combine
                            FlowDocument flowDoc = new FlowDocument();
                            flowDoc.Blocks.AddRange(blocks);
                            // viewer
                            FlowDocumentScrollViewer viewer = new FlowDocumentScrollViewer()
                            {
                                Document = flowDoc
                            };
                            lst.Add(viewer);
                            // clear
                            blocks.Clear();
                        }
                        // add current element
                        lst.Add(e);
                    }
                }

                elements = lst;
            }

            if ((acceptType & AcceptType.UIElement) == 0 && elements.OfType<UIElement>().Count() != 0)
            {
                if((acceptType & AcceptType.Inline) != 0)
                {
                    List<object> lst = new List<object>();
                    foreach(var e in elements)
                        if(e is UIElement)
                            lst.Add(new InlineUIContainer((UIElement)e));
                        else
                            lst.Add(e);
                    elements = lst;
                }
                else if ((acceptType & AcceptType.Block) != 0)
                {
                    List<object> lst = new List<object>();
                    foreach(var e in elements)
                        if(e is UIElement)
                            lst.Add(new BlockUIContainer((UIElement)e));
                        else
                            lst.Add(e);
                    elements = lst;
                }
                else
                {
                    throw new InvalidOperationException("Cannot convert UIElement");
                }
            }

            return elements;
        }
    }
}
